package com.simulador.server;

import java.util.ArrayList;
import java.util.Hashtable;


public class Proyecto{
	
	//variables para graficar()
	boolean[] graficados;
	Proteina primera; //la primera proteina del pathlike
	ArrayList<Proteina> agraficar;
		
	String nombreDeArchivo;
	ArrayList<Proteina> proteinas;	//-Proteinas que se toman en cuenta para el proyecto (wilds + mutantes)
	Hashtable<String, Integer> medioExterno; //list medioExterno (Alimento, drogas, etc...)
	ArrayList<String> fenotipo; //sensible, no sensible, otros?
	
	/**
	 * CONSTRUCTOR
	 * Para que el usuario cree un nuevo proyecto
	 * @param Proteinas que el usuario solicita (idUniversal)
	 * 
	 * Abrir entorno: da la lista de organismos que tiene y 
	 * al seleccionar uno preguntar el pathway en el que se quiere enfocar
	 */
	public Proyecto(String semilla, int expander){ //Después agregar: Hashtable<String, Integer> medioExternos, ArrayList<String> fenotipos
		nombreDeArchivo = "proyectoX";
		
		proteinas = new ArrayList<Proteina>();	
		addprot(semilla,true, 1, true, expander);
		
		//medioExterno = medioExternos;
		//fenotipo = fenotipos;
	}
	
	/**
	 * Agrega una proteina al proyecto
	 * llamado
	 * -al crear un nuevo proyecto
	 * -desde gui por el usuario en cualquier momento (llamar a relacionadas)
	 * @param protID proteína a agregar
	 * @param visible si será o no visible al graficar
	 * @param transparente la transparencia de la proteína al graficar
	 * @param relacionadas si se agregarán las proteinas relacionadas
	 * @param expander el número de niveles en los que las proteínas serán visibles
	 * 
	 * Aquí se pondría el nivel de visibilidad según la relevancia (o quiza al crear la proteína más bien)
	 * podría ser llamado desde new Proteina
	 */
	public void addprot(String protID, boolean visible, double transparente, boolean relacionadas, int expander){
		Proteina prot = null;
		if(this.getProt(protID) == null){ //solo agregar si no se había agregado la proteína previamente
			if(!DB.buscar(protID)){ //si no se encuentra esa proteina en la base de datos:
				System.err.println("No se encuentra la proteína \""+ protID +"\" en la base de datos");	
			}else{
				prot = new Proteina(protID, visible, transparente);
				proteinas.add(prot);
				if(relacionadas){ relacionadas(prot, expander);}
			}
		}else{
			//si algo más se modifica en relacionadas poner aquí
			if(visible){
				this.getProt(protID).visible =visible;
			}
			prot = this.getProt(protID);
		}
		//TODO
		//si ya se agrego la proteína de todos modos debe llamara a expander por si esta es una ruta más corta
		//if(relacionadas){ relacionadas(prot, expander);} //pero causa Stack overflow
	}
	
	/**
	 * metodo para encontrar y agregar al proyecto 
	 * las proteínas relacionadas a una proteína específica
	 * @param prot la proteína a la que se le buscará su rio arriba y abajo
	 * @param expander el número de niveles en los que las proteínas serán visibles
	 */
	public void relacionadas(Proteina prot, int expander){
		//busca en base de datos las proteinas relacionadas
		ArrayList<String> rel = DB.getRioAbajo(prot.getID_universal());		
		rel.addAll(DB.getRioArriba(prot.getID_universal()));
		for(int i=0; i<rel.size(); i++){
			boolean visible;
			if(expander>0){
				visible = true; //si se modifica algo más, agregarlo en el else de addprot
			}else{
				visible= false;
			}
			addprot(rel.get(i),visible, prot.transparencia -0.05, true, expander-1); //Disminuye la transparencia progresivamente
		}
	}
	
	// TODO public void addprot(todos los datos){ crea una prot sin consultar DB
	
	/**
	 * Obtener una proteína por ID_universal
	 */
	public Proteina getProt(String IDu){
		Hashtable<String, Proteina> prothash = new Hashtable<String, Proteina>(proteinas.size());
		for(int i=0; i<proteinas.size(); i++){
			prothash.put(proteinas.get(i).ID_universal, proteinas.get(i));
		}
		return prothash.get(IDu);
	}
	
	/**
	 * proteinas a JSON nodos + edges
	 * @return arreglo con solo un objeto: "data"
	 */
	public String graficarData(){
		//nodes
		String nodes = "[";
		//edges
		String edges = "[";
		
		boolean primeroNodes = true;
		boolean primeroEdges = true;
		for(int i=0; i<this.proteinas.size(); i++){
			if(this.proteinas.get(i).visible){
				//nodo
				//esto es para que la ultima linea no tenga coma, pero ya vi que puede llevarla y no pasa nada
				if(primeroNodes){
					primeroNodes=false;
				}else{
					nodes=nodes+", ";
				}
				nodes= nodes+this.proteinas.get(i).nodoJSON();
				//edges
				for(String rio : this.proteinas.get(i).rioAbajo){
					if(getProt(rio) == null){ //borrar todo el if
						System.out.print("#proteinas "+proteinas.size());
					}
					if(getProt(rio).visible){
						if(primeroEdges){
							primeroEdges=false;
						}else{
							edges=edges+", ";
						}
						edges = edges+this.proteinas.get(i).edgeJSON(getProt(rio));
					}
				}
			}
		}
		nodes = nodes + "]";
		edges = edges + "]";
		String data = "{nodes: " + nodes+", edges: "+ edges + "}";
		
		//eval() no me agarra los objects pero si los arrays :S
		data = "["+data+"]";
		return data;
	}
	

	/**
	 * Ordena visiblemente las proteínas en pathslikes
	 * Le pone las cordenadas a cada proteína
	 */
	//tal vez se pueda cambiar primera a String ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public void graficarXY(){
		int posPrimeraY = 50; //CALIBRAR
		//crear lista de porteínas a graficar (no todas las del proyecto)
		agraficar = new ArrayList<Proteina>();
		for(int i=0; i<this.proteinas.size(); i++){
			if(this.proteinas.get(i).visible){
				agraficar.add(this.proteinas.get(i));
			}
		}
		
		//agarra una proteína al azar y saca sus rioArriba recursivamente hasta llegar a uno que no tenga proteinas VISIBLES rio arriba
		graficados = new boolean[agraficar.size()]; //arreglo que indica si fueron graficados los elementos de agraficar
		for(int i=0; i<agraficar.size(); i++){
			graficados[i] = false;
		}

		for(int i=0; i<agraficar.size(); i++){ //recorre toda la lista a graficar
			if(!graficados[i]){ //si aun no se grafica...
				primera = agraficar.get(i);
				if(agraficar.get(i).rioArriba.size() > 0); //para ver si ya estas en una punta
				for(int j=0; j<agraficar.get(i).rioArriba.size(); j++){ //recorre el io arriba
					if(this.getProt(agraficar.get(i).rioArriba.get(j)).visible 	//para ver si las que estan rioarriba son visibles
							&& !graficados[agraficar.indexOf(this.getProt(agraficar.get(i).rioArriba.get(j)))] ){ //y no han sido graficadas
						primera = this.getProt(agraficar.get(i).rioArriba.get(j)); //primera se vuelve la proteína rio arriba visible y no graficada
						return;//Funcionaría sin el return pero para eficientizar y no seguir buscando despues de la primera exitosa (la intencion es que solo rompa el primer for)
					}
				}
				//graficar las proteínas que van hasta la izquierda
				
				
				//graficar rio abajo de primera
				graficarAbajo(primera, 0, posPrimeraY);
				posPrimeraY += 50; //CALIBRAR
			}
		}
	}
	
	/**
	 * Para graficar proteínas rio abajo 
	 * (y cuando halla varias ubicaciones??? se debe cambiar el metodo  getProt y que regrese una lista de prots
	 * una prot por c/ubicacion
	 * @param prot
	 */
	private void graficarAbajo(Proteina prot, int x, int y){
		//grafica prot
		//this.getProt(prot.getID_universal()).setPosX(0); //empiezan a la izquierda
		prot.posX= x;
		//this.getProt(prot.getID_universal()).setPosY(posPrimeraY); //define Y de acuerdo a las otras ya graficadas
		prot.posY= y;
		graficados[agraficar.indexOf(prot)] = true; //marca que ya esta graficado
				
		//grafica rio abajo
		for(int j=0; j<prot.rioAbajo.size(); j++){
			if(graficados[agraficar.indexOf(this.getProt(prot.rioAbajo.get(j)))]){ //si no esta graficado...
				graficarAbajo(this.getProt(prot.rioAbajo.get(j)), x+160, y+(int)(Math.pow(-1, j) *(j/2*100))); //MEJORAR
			}
		}
	}
	
	/**
	 * ejecuta el experimento y dice el fenotipo
	 */
	public void simular(){
		//TODO ejecutar proteínas de membrana
	}
	

}
